package com.hzjq.core.serial

import com.hzjq.core.BlastDelegate
import com.hzjq.core.bean.ErrorResult
import com.hzjq.core.callback.Callback
import com.hzjq.core.event.PioUnEnableEvent
import com.hzjq.core.impl.CheckerImpl
import com.hzjq.core.massage.DataMessageBean
import com.hzjq.core.receive.Receiver
import com.hzjq.core.receive.Receives
import com.hzjq.core.util.OtgUtils
import io.reactivex.Observable
import io.reactivex.ObservableEmitter
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.functions.Consumer
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import java.util.concurrent.TimeUnit

class SerialManager {

    private constructor()

    private object B {
        val sm = SerialManager()
    }

    companion object {

        /**
         * 获取串口入管理口类，单例
         */
        fun getInstance(): SerialManager {
            return B.sm
        }
    }

    private var powerOn = -1
    private var portOpenState = -1
    private var fialdDisposable: Disposable? = null


    /**
     * 串口是否已打开
     */
    fun isOpenPort(): Boolean {
        return portOpenState == 0
    }

    /**
     * 底板电源是否已打开
     */
    fun isOpenPower(): Boolean {
        return powerOn == 0
    }

    /**
     * 上电，返回值小于0 则打开是失败
     */
    fun openPower(onNext: Consumer<Int>): Disposable {
        return Observable.create<Int> {
            OtgUtils.setDT40CGPIOEnabled(true)
            this.powerOn = 0
            it.onNext(powerOn)
        }.subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(onNext)
    }

    /**
     * 失败时重试3次
     */
    private fun openPio(e: ObservableEmitter<Int>) {
        releaseFailedDisposable()
        var count = 0
        fialdDisposable = Observable.interval(300, 300, TimeUnit.MILLISECONDS)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .map {
                if (OtgUtils.setDT40CGPIOEnabled(true)) {
                    this.powerOn = 0
                    e.onNext(powerOn)
                    releaseFailedDisposable()
                } else {
                    if (count == 3) {
                        this.powerOn = -1
                        e.onNext(powerOn)
                        releaseFailedDisposable()
                    }
                    count++
                }
            }.subscribe()
    }


    /**
     * 停止上电失败重试
     */
    private fun releaseFailedDisposable() {
        if (fialdDisposable != null && !fialdDisposable!!.isDisposed) {
            fialdDisposable?.dispose()
        }
    }



    /**
     * 打开串口
     * @param portStr   串口号
     * @param ibaudRate 波特率
     *
     * @return 0：打开串口成功
     *        -1：无法打开串口：没有串口读/写权限！
     *        -2：无法打开串口：未知错误！
     *        -3：无法打开串口：参数错误！
     */
    fun openPort(): Int {
        portOpenState = BlastSerial.instance()!!.open("/dev/ttyHSL0", 115200)
        return portOpenState
    }

    /**
     * 关闭串口
     */
    fun closePort() {
        BlastSerial.instance()?.clearAllDataListener()
        BlastSerial.instance()!!.close()
        portOpenState = -1
    }


    /**
     * 模块上电
     */
    fun enablePio(enable: Boolean) {
        if (!enable) {
            sendBack()
        }
        OtgUtils.setDT40CGPIOEnabled(enable)
    }


    private fun sendBackResult() {
        this.powerOn = -1
        this.portOpenState = -1

        Receives.getInstance().unRegisterReceiver(
            BlastDelegate.getDelegate().getAssemblyCmdLoader()
                .backCmd()
        )
        EventBus.getDefault().post(PioUnEnableEvent())
    }

    private fun sendBack() {
        val cmd = DataMessageBean(
            BlastDelegate.getDelegate().getAssemblyCmdLoader()
                .backCmd().cmd
        )

        Receives.getInstance().registerReceiver(BlastDelegate.getDelegate().getAssemblyCmdLoader()
            .backCmd(), object : Receiver {
            override fun convert(msg: String): Any {
                return msg
            }

            override fun onSuccess(msg: Any) {
                sendBackResult()
            }

            override fun failed() {

            }
        })

        BlastDelegate.getDelegate().getCmdExeLoader()
            .exePollResultCmd(cmd.assembly(), object : Callback<Boolean> {
                override fun onResult(t: Boolean) {
                    sendBackResult()
                }

                override fun onRetryCountChanged(retryCount: Int, action: String) {
                    sendBackResult()
                }

                override fun onError(errorCode: ErrorResult) {

                }

            })
    }


}